#pragma once

#include <vector>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

class Channel
{
public:
    Channel(int wfd, int pid)
        : _wfd(wfd), _pid(pid)
    {
    }
    ~Channel()
    {
    }
    int Wfd() const { return _wfd; }
    int Pid() const { return _pid; }

    void Close() const { close(_wfd); }
    void Wait() const { waitpid(_pid, nullptr, 0); }

private:
    int _wfd; // 控制子进程
    int _pid; // 拿到子进程pid，方便回收
};

class ChannelManager
{
public:
    ChannelManager() : _next(0)
    {
    }

    // 为了保证负载均衡，采用轮询的方式
    const Channel& SelectChannel()
    {
        const Channel& c = _channels[_next];
        // 选出下标
        ++_next;
        _next %= _channels.size();
        return c;
    }

    void InsertChannel(int wfd, int pid)
    {
        _channels.emplace_back(wfd, pid);
    }

    void CloseFd() const
    {
        for(const auto &e : _channels)
        {
            e.Close();
        }
    }

    void CloseChannels() const
    {
        // 2.正着关，创建子进程时就关闭从父进程继承下来的wfd
        for (size_t i = 0; i < _channels.size(); i++)
        {
            // 关闭父进程的写fd，让子进程读到0个字节退出。
            _channels[i].Close();
            _channels[i].Wait();
            printf("等待成功，回收了子进程：%d\n", _channels[i].Pid());
        }

        // // 1.倒着关闭
        // for (int i = _channels.size() - 1; i >= 0; i--)
        // {
        //     // 关闭父进程的写fd，让子进程读到0个字节退出。
        //     _channels[i].Close();
        //     _channels[i].Wait();
        //     printf("等待成功，回收了子进程：%d\n", _channels[i].Pid());
        // }
    }

    ~ChannelManager()
    {
    }

private:
    std::vector<Channel> _channels;
    int _next;
};

class ProcessPool
{
public:
    ProcessPool()
    {
    }
    ~ProcessPool()
    {
    }

    void ChildRead(int rfd) const
    {
        int taskcode = 0;
        while (true)
        {
            ssize_t ret = read(rfd, &taskcode, sizeof(taskcode));
            // 父进程写端关闭了，子进程要结束
            if (ret == 0)
            {
                std::cout << "父进程写端关闭，子进程:" << getpid() << "退出" << std::endl;
                break;
            }
            // 读到的不是4字节，丢弃，重新读
            if (ret != sizeof(taskcode))
            {
                printf("丢弃\n");
                return;
            }
            // 执行相应任务
            printf("进程：%d ExcuteTask开始，ret:%d，taskcode:%d\n", getpid(), (int)ret, taskcode);
            _tm.ExecuteTask(taskcode);
        }
    }

    void Create(int num)
    {
        for (int i = 0; i < num; i++)
        {
            // 1.创建管道
            int pipefd[2] = {0};
            int ret = pipe(pipefd);
            if (ret != 0)
                exit(1);
            // 2.创建子进程
            pid_t pid = fork();
            // 3.关闭父读，子写
            if (pid < 0)
                exit(1);
            else if (pid == 0)
            {
                // 关掉从父进程继承下来的wfd
                _cm.CloseFd();
                // 子进程关闭写
                close(pipefd[1]);
                // 子进程工作
                printf("ChildRead开始，进程为%d\n", getpid());
                ChildRead(pipefd[0]);
                close(pipefd[0]);
                // 子进程完成工作，退出
                exit(0);
            }
            // 父进程关闭读
            close(pipefd[0]);
            // emplace_back直接构造，插入到_channels
            _cm.InsertChannel(pipefd[1], pid);
            // 循环num次
        }
    }

    // 选择一个子进程，随机发送任务
    void Run()
    {
        // 1.选择一个子进程
        const Channel &c = _cm.SelectChannel();
        printf("挑选的子进程为：%d\n", c.Pid());
        // 2.获取任务码
        int taskcode = _tm.TaskCode();
        // 3.发送任务码给子进程，子进程执行(写给子进程)
        printf("父进程：%d 写入taskcode：%d\n", getpid(), taskcode);
        ssize_t ret = write(c.Wfd(), &taskcode, sizeof(taskcode));
    }

    void Close() const
    {
        _cm.CloseChannels();
    }

private:
    TaskManager _tm;
    ChannelManager _cm;
};
